home *** CD-ROM | disk | FTP | other *** search
/ Resource Library: Multimedia / Resource Library: Multimedia.iso / maestro / source / timeline / drawcnvs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-15  |  22.3 KB  |  495 lines

  1. /*
  2.  * Copyright (c) 1990, 1991 Stanford University
  3.  *
  4.  * Permission to use, copy, modify, and distribute this software and 
  5.  * its documentation for any purpose is hereby granted without fee, provided
  6.  * that (i) the above copyright notices and this permission notice appear in
  7.  * all copies of the software and related documentation, and (ii) the name
  8.  * Stanford may not be used in any advertising or publicity relating to
  9.  * the software without the specific, prior written permission of
  10.  * Stanford.
  11.  * 
  12.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  13.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  14.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  15.  *
  16.  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
  17.  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
  18.  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
  19.  * ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
  20.  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  21.  * SOFTWARE.
  22.  */
  23.  
  24. /* $Header: /Source/Media/collab/TimeLine/RCS/drawCanvas.c,v 1.0 91/09/30 16:54:25 chua Exp Locker: drapeau $ */
  25. /* $Log:    drawCanvas.c,v $
  26.  * Revision 1.0  91/09/30  16:54:25  chua
  27.  * Update to version 1.0
  28.  * 
  29.  * Revision 0.49  91/09/26  18:00:18  chua
  30.  * In MouseDownEvents, if the click was on a note, do not deselect it if it
  31.  * was already selected.
  32.  * 
  33.  * Revision 0.48  91/09/23  17:08:50  chua
  34.  * Deselect any selected note when positioning the playback head.
  35.  * 
  36.  * Revision 0.47  91/09/20  12:59:33  chua
  37.  * In DrawPlaybackHead, add back 2 braces in the while (sec >= 60) statement,
  38.  * which were accidentally taken out.
  39.  * 
  40.  * Revision 0.46  91/09/19  17:28:42  chua
  41.  * Make sure that variables are initialized properly.  Change formatting slightly,
  42.  * so that (if, for, while) statements with only one statement in them will not have
  43.  * braces.
  44.  * 
  45.  * Revision 0.45  91/09/16  14:38:42  chua
  46.  * In DrawCanvasEventHandler, when checking if an event is a keyboard event, also check
  47.  * if the erase key is being pressed, other than checking for the left keypad and
  48.  * ascii keys.
  49.  * 
  50.  * Revision 0.44  91/08/21  16:52:09  chua
  51.  * Modified DrawCanvasEventHandler to take care of double click events. 
  52.  * Double clicking on a note will bring up the notes info popup window.
  53.  * 
  54.  * Revision 0.43  91/08/20  16:15:51  chua
  55.  * In MouseUpEvents, if the mouse was dragged over a region, check if the region is too small.
  56.  * If it is, just position the playback head instead of highlighting a region.
  57.  * 
  58.  * Revision 0.42  91/08/19  19:17:55  chua
  59.  * Added a noteOffset variable to keep track of where the mouse is in a note when
  60.  * dragging it.
  61.  * 
  62.  * Revision 0.41  91/08/16  16:57:51  chua
  63.  * *** empty log message ***
  64.  * 
  65.  * Revision 0.40  91/08/16  16:56:59  chua
  66.  * This version contains the ScrollTimerNotify routine and also the DrawCanvas repaint
  67.  * and event handlers.
  68.  *  */
  69.  
  70. static char drawCanvasrcsid[] = "$Header: /Source/Media/collab/TimeLine/RCS/drawCanvas.c,v 1.0 91/09/30 16:54:25 chua Exp Locker: drapeau $";
  71.  
  72. #include "main.h"
  73. #include <sys/time.h>
  74.  
  75. static int timerOn = 0;                            /* Indicates if the scrolling timer is on */
  76. static int wasLeftDown = 0;
  77. static int selectNote = 0;                        /* Indicates if a note has been selected and may be dragged */
  78. static Instrument *currentInst;                        /* Pointer to the chosen instrument node */
  79. static struct itimerval timer;                        /* Timer for playback purposes */
  80. static TimeLineFramePtr currenttlFrame;                    /* Pointer to current TimeLine document being played */
  81. static int noteOffset = 0;                        /* Indicates where the mouse is on a note when dragging it. */
  82.  
  83.  
  84. /*
  85.  * This function handles keyboard events.  Please refer to the comment heading for the DrawCanvasEventHandler for more information.
  86.  */
  87. void CheckKeyboardEvents(tlFrame, event, item)
  88.      TimeLineFramePtr tlFrame;
  89.      Event *event;
  90.      Menu_item item;
  91. {
  92.   switch (event_action(event)) 
  93.   {
  94.    case ACTION_ERASE_CHAR_BACKWARD:
  95.     DeleteNotesFromList(1, tlFrame);
  96.     break;
  97.    case ACTION_COPY:
  98.     CopyHandler(item, MENU_NOTIFY);
  99.     break;
  100.    case ACTION_PASTE:
  101.     PasteHandler(item, MENU_NOTIFY);
  102.     break;
  103.    case ACTION_CUT:
  104.     CutHandler(item, MENU_NOTIFY);
  105.     break;
  106.    default:
  107.     break;
  108.   }
  109. }
  110.  
  111. /*
  112.  * This function handles mouse down events.  Please refer to the comment heading for the DrawCanvasEventHandler for more information.
  113.  */
  114. void MouseDownEvents(tlFrame, xPos, yPos, event)
  115.      TimeLineFramePtr tlFrame;
  116.      int xPos;
  117.      int yPos;
  118.      Event *event;
  119. {
  120.   int noteX;
  121.   int noteSelect;
  122.   int done;
  123.   int doubleClick;
  124.   
  125.   selectNote = 0;
  126.   timerOn = 0;
  127.   wasLeftDown = 1;
  128.   scrollHorStart = xv_get(tlFrame->DrawScrollbarHor, SCROLLBAR_VIEW_START); /* Get the canvas window horizontal and vertical start and end points */
  129.   scrollHorEnd = scrollHorStart + xv_get(tlFrame->DrawScrollbarHor, SCROLLBAR_VIEW_LENGTH);
  130.   scrollVerStart = xv_get(tlFrame->DrawScrollbarVer, SCROLLBAR_VIEW_START);
  131.   scrollVerEnd = scrollHorStart + xv_get(tlFrame->DrawScrollbarVer, SCROLLBAR_VIEW_LENGTH);
  132.   if (tlFrame->areaSelected)                        /* Clear the previously selected area */
  133.     XFillRectangle(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gcLine, 
  134.            (tlFrame->startX  / tlFrame->zoomLevel) - tlFrame->canvasStart, 
  135.            tlFrame->startY, (tlFrame->endX - tlFrame->startX) / tlFrame->zoomLevel,
  136.            tlFrame->endY - tlFrame->startY);
  137.   DrawPlaybackHead(-1, tlFrame);
  138.   tlFrame->areaSelected = 0;
  139.   SetStartEndRegion(tlFrame, 0, 0);
  140.   tlFrame->startX = xPos;
  141.   tlFrame->startY = yPos;
  142.   tlFrame->endX = xPos;
  143.   tlFrame->endY = yPos;
  144.   done = 0;
  145.   currentInst = tlFrame->instHead;
  146.   while (currentInst != NULL && !done)                    /* Find which instrument has been selected */
  147.   {
  148.     if (event_y(event) >= currentInst->cableStart - NoteHeight/2 && 
  149.     event_y(event) <= currentInst->cableStart + NoteHeight/2)   
  150.       done = 1;
  151.     else 
  152.       currentInst = currentInst->next;
  153.   }
  154.   if (currentInst != NULL) 
  155.   {
  156.     wasLeftDown = 0;
  157.     if (event_id(event) == MS_LEFT)                    /* Check if left mouse button is pressed */
  158.     {
  159.       doubleClick = CheckDoubleClick(event, tlFrame);
  160.       noteX = event_x(event);
  161.       noteSelect = CheckNoteSelected(currentInst, (noteX + tlFrame->canvasStart) * tlFrame->zoomLevel, tlFrame, doubleClick);
  162.       if (noteSelect == 0)
  163.       {
  164.     if (tlFrame->gridSpacing > 0)                    /* Snap to grid line if necessary */
  165.       noteX = noteX - noteX % tlFrame->gridSpacing;
  166.     noteX = (noteX + tlFrame->canvasStart) * tlFrame->zoomLevel;
  167.     AddandDisplayNewNote(currentInst, noteX, tlFrame);
  168.       }
  169.       else                                /* Note selected.  May be dragging it */
  170.       {
  171.     if (doubleClick == OK)                       
  172.       ShowInfoWindow(currentInst, tlFrame);
  173.     if (xv_get(tlFrame->TimeLine_window->controls, PANEL_CLIENT_DATA) != 0)
  174.     {
  175.       wasLeftDown = 1;
  176.       selectNote = 1;
  177.       noteOffset = event_x(event) - ((currentInst->infoNote->start / tlFrame->zoomLevel) - tlFrame->canvasStart);
  178.     }
  179.       }
  180.     }
  181.     else if (event_id(event) == MS_MIDDLE &&                /* Middle button is pressed. Delete a note */
  182.          xv_get(tlFrame->TimeLine_window->controls, PANEL_CLIENT_DATA) != 0) /* Only if this is not the clipboard document */
  183.       DeleteNote(currentInst, xPos, tlFrame);
  184.   }
  185. }
  186.  
  187. /*
  188.  * This function handles mouse up events.  Please refer to the comment heading for the DrawCanvasEventHandler for more information.
  189.  */
  190. void MouseUpEvents(tlFrame, xPos)
  191.      TimeLineFramePtr tlFrame;
  192.      int xPos;
  193. {
  194.   if (wasLeftDown > 1)                            /* Some dragging has been done */
  195.   {
  196.     if (timerOn == 1) 
  197.     {
  198.       timerOn = 0;
  199.       notify_set_itimer_func(currenttlFrame->TimeLine_window->window, /* Turn timer off */
  200.                  NOTIFY_FUNC_NULL, ITIMER_REAL, 
  201.                  NULL, NULL);
  202.     }
  203.     if (selectNote == 0)                        /* Draw the selected region */
  204.     {
  205.       
  206.       XDrawRectangle(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gcLine, (tlFrame->startX  / tlFrame->zoomLevel) - tlFrame->canvasStart, 
  207.              tlFrame->startY, (tlFrame->endX - tlFrame->startX) / tlFrame->zoomLevel,
  208.              tlFrame->endY - tlFrame->startY);
  209.       DeselectNote(tlFrame);                        /* Deselect any currently selected note first */
  210.       if (abs((tlFrame->endX - tlFrame->startX) / tlFrame->zoomLevel) < DistanceThreshold) 
  211.     DrawPlaybackHead(tlFrame->endX, tlFrame);
  212.       else 
  213.     DrawSelectArea(tlFrame);
  214.     }
  215.     else                                /* Draw the note at the new position */
  216.       DrawMoveNote (currentInst, tlFrame, noteOffset);
  217.   }
  218.   else if (wasLeftDown == 1) 
  219.   {                                    /* Draw playback head as mouse was not dragged */
  220.     if (selectNote == 0) 
  221.     {
  222.       DeselectNote(tlFrame);                        /* Deselect any currently selected note first */
  223.       DrawPlaybackHead(xPos, tlFrame);
  224.     }
  225.     if (tlFrame->noteSelected == 1)                    /* If note was selected, set the area selected to that of the note */
  226.     {
  227.       tlFrame->startX = tlFrame->startnoteX;
  228.       tlFrame->endX = tlFrame->endnoteX;
  229.       tlFrame->startY = tlFrame->startnoteY;
  230.       tlFrame->endY = tlFrame->endnoteY;
  231.     }
  232.   }
  233.   wasLeftDown = 0;
  234. }
  235.  
  236. /*
  237.  * This function is the timer notify procedure to check if scrolling of the canvas needs to be done when the user is attempting to select an area.
  238.  * This occurs when the user drags the mouse off the canvas.  The canvas should scroll even if the mouse is held still, which is why a timer procedure
  239.  * is needed.  The function will check if we are dragging an area or a selected note.
  240.  */
  241. Notify_value ScrollTimerNotify()
  242. {
  243.   Rect *r;
  244.   int x, y;
  245.   int xStart, xLength;
  246.   int yStart;
  247.   
  248.   xStart = (int) xv_get (currenttlFrame->DrawScrollbarHor, SCROLLBAR_VIEW_START);
  249.   yStart = (int) xv_get (currenttlFrame->DrawScrollbarVer, SCROLLBAR_VIEW_START);
  250.   xLength = xv_get(currenttlFrame->DrawScrollbarHor, SCROLLBAR_VIEW_LENGTH);
  251.   r = (Rect *) xv_get(currenttlFrame->TimeLine_window->DrawCanvas, WIN_MOUSE_XY);
  252.   x = r->r_left + xStart;
  253.   if (x > xStart + xLength) 
  254.     x = xStart + xLength;
  255.   if (x < xStart) 
  256.     x = xStart;
  257.   y = r->r_top + yStart;
  258.   y = y - y % (IconHeight + IconGap);
  259.   if (selectNote == 1)                            /* Dragging a note */
  260.   {
  261.     XDrawRectangle(currenttlFrame->dpyDraw, currenttlFrame->xidDraw, currenttlFrame->gcLine, 
  262.            (currenttlFrame->startX  / currenttlFrame->zoomLevel) - currenttlFrame->canvasStart - noteOffset, 
  263.            currentInst->cableStart - NoteHeight/2, 
  264.            (currentInst->infoNote->ms->duration * PixelsPerSecond) / (2 * currenttlFrame->zoomLevel),
  265.            NoteHeight);
  266.     currenttlFrame->startX = (x + currenttlFrame->canvasStart) * currenttlFrame->zoomLevel;
  267.     if (currenttlFrame->startX / currenttlFrame->zoomLevel - noteOffset < 0) 
  268.       currenttlFrame->startX = noteOffset * currenttlFrame->zoomLevel;
  269.   }
  270.   else                                    /* Dragging an area */
  271.   {
  272.     XDrawRectangle(currenttlFrame->dpyDraw, currenttlFrame->xidDraw, currenttlFrame->gcLine, 
  273.            (currenttlFrame->startX  / currenttlFrame->zoomLevel) - currenttlFrame->canvasStart, 
  274.            currenttlFrame->startY, (currenttlFrame->endX - currenttlFrame->startX) / currenttlFrame->zoomLevel,
  275.            currenttlFrame->endY - currenttlFrame->startY);
  276.     currenttlFrame->endX = (x + currenttlFrame->canvasStart) * currenttlFrame->zoomLevel;
  277.     if (y < currenttlFrame->startY)                    /* If the new Y position is smaller (higher) than the start position, */
  278.       currenttlFrame->endY = y;
  279.     else 
  280.       currenttlFrame->endY = y + (IconHeight + IconGap);
  281.     if (currenttlFrame->endY > 
  282.     (currenttlFrame->numberOfApps * (IconHeight + IconGap)))    /* Set the maximum y position to be the bottom line of the last instrument track */
  283.       currenttlFrame->endY = currenttlFrame->numberOfApps * (IconHeight + IconGap);
  284.     else if (currenttlFrame->endY < 0)                    /* Check that the Y position does not go off the canvas */
  285.       currenttlFrame->endY = 0;
  286.   }
  287.   if (x > scrollHorStart && x < scrollHorEnd &&
  288.       currenttlFrame->endY > scrollVerStart && currenttlFrame->endY < scrollVerEnd) 
  289.   {
  290.     timerOn = 0;
  291.     notify_set_itimer_func(currenttlFrame->TimeLine_window->window, /* Turn off the timer */
  292.                NOTIFY_FUNC_NULL, ITIMER_REAL, 
  293.                NULL, NULL);
  294.   }
  295.   CheckHorizontalScrolling(x, currenttlFrame);
  296.   CheckVerticalScrolling(currenttlFrame->endY, currenttlFrame);
  297.   if (selectNote == 1) 
  298.     XDrawRectangle(currenttlFrame->dpyDraw, currenttlFrame->xidDraw, currenttlFrame->gcLine, 
  299.            (currenttlFrame->startX  / currenttlFrame->zoomLevel) - currenttlFrame->canvasStart - noteOffset, 
  300.            currentInst->cableStart - NoteHeight/2, 
  301.            (currentInst->infoNote->ms->duration * PixelsPerSecond) / (2 * currenttlFrame->zoomLevel),
  302.            NoteHeight);
  303.   else 
  304.     XDrawRectangle(currenttlFrame->dpyDraw, currenttlFrame->xidDraw, currenttlFrame->gcLine, 
  305.            (currenttlFrame->startX / currenttlFrame->zoomLevel) - currenttlFrame->canvasStart, 
  306.            currenttlFrame->startY, (currenttlFrame->endX - currenttlFrame->startX) / currenttlFrame->zoomLevel,
  307.            currenttlFrame->endY - currenttlFrame->startY);
  308.   return NOTIFY_DONE;
  309. }
  310.  
  311. /*
  312.  * Event callback function for `DrawCanvas'.
  313.  * This function will first check if the timeline is in stop mode.  If not, it will return.
  314.  * If the mouse has just entered the canvas, the canvas will grab the keyboard focus so that any key typed would be detected by it.
  315.  * If the Delete or Backspace key is pressed, this has the same effect as choosing the Delete item from the edit menu.
  316.  * If the Copy, Paste or Cut button on the left keypad is pressed, the relevant edit functions are performed.
  317.  * If the left mouse button is pressed, the function checks if it is pressed on the instrument cable (with the allowance of the height of a note).
  318.  * If so, it will add a new note starting at where the mouse was pressed, if there was no note already existing at where the mouse click was.
  319.  * If a note exist, this note will be selected.
  320.  * If not, it will reposition the playback head to where the mouse was pressed.
  321.  * If the mouse was dragged, a shaded rectangle area covering the space over which the mouse was dragged will be drawn.   This represents the selected area
  322.  * on which edit functions (cut, paste, etc) can now be done.  Also, if an area was selected, any previously selected note would be deselected.  This
  323.  * is to avoid confusion, since the edit functions can also apply to a selected note.
  324.  * Dragging can also be applied to a note.  This is indicated by the variable selectNote, which is set to 1 if a note has been selected, 0 otherwise.
  325.  * A check is also made to see if the mouse was dragged off the canvas.  If so, a timer is started and this timer is called every 1/5th of a second to
  326.  * check if scrolling needs to be done.
  327.  * If the middle mouse button was pressed and it is near the chosen instrument cable, the note that the mouse was on will be deleted.  If there is no
  328.  * note, nothing will be done.  A note will only be deleted if it does not belong on the clipboard document, meaning clicking the middle button on the
  329.  * clipboard canvas will have no effect.
  330.  */
  331. Notify_value DrawCanvasEventHandler(win, event, arg, type)
  332.      Xv_window           win;
  333.      Event               *event;
  334.      Notify_arg          arg;
  335.      Notify_event_type     type;
  336. {
  337.   int xPos, yPos;
  338.   TimeLineFramePtr tlFrame;
  339.   TimeLine_window_objects    *ip = (TimeLine_window_objects *) xv_get(win, XV_KEY_DATA, INSTANCE);
  340.  
  341.   tlFrame = TimeLineWindow[xv_get(ip->controls, PANEL_CLIENT_DATA)];
  342.   xPos = (event_x(event) + tlFrame->canvasStart) * tlFrame->zoomLevel;
  343.   yPos = event_y(event) - event_y(event) % (IconHeight + IconGap);  /* Move y position to the nearest track starting y position */
  344.   if (tlFrame->status == StopMode)                    /* Only execute the following if the TimeLine is in Stop mode */
  345.   {
  346.     if (event_id(event) == LOC_WINENTER)                /* Grab the keyboard focus for the canvas */
  347.       win_set_kbd_focus(win, xv_get(win, XV_XID));
  348.     else if ((event_is_ascii(event) || event_is_key_left(event) 
  349.           || event_action(event) == ACTION_ERASE_CHAR_BACKWARD) &&
  350.          event_is_down(event))
  351.       CheckKeyboardEvents(tlFrame, event, ip->documentButton);
  352.     else if (event_is_down(event)                    /* Left or middle mouse button is down */
  353.          && (event_id(event) == MS_LEFT ||
  354.          event_id(event) == MS_MIDDLE)) 
  355.       MouseDownEvents(tlFrame, xPos, yPos, event);
  356.     else if (event_is_up(event))
  357.       MouseUpEvents(tlFrame, xPos);
  358.     else if (event_id(event) == LOC_DRAG && wasLeftDown && timerOn == 0) 
  359.     {
  360.       if (event_x(event) <= scrollHorStart || event_x(event) >= scrollHorEnd ||
  361.       event_y(event) <= scrollVerStart || event_y(event) >= scrollVerEnd) 
  362.       {
  363.     currenttlFrame = tlFrame;
  364.     timerOn = 1;
  365.     timer.it_value.tv_sec = 0;                    /* Set the timer values to notify every 1/5th of a second */
  366.     timer.it_value.tv_usec = 200000;                /* The timer notify procedure will check to see if scrolling needs to be done */
  367.     timer.it_interval.tv_sec = 0;
  368.     timer.it_interval.tv_usec = 200000;
  369.     notify_set_itimer_func(tlFrame->TimeLine_window->window, ScrollTimerNotify, ITIMER_REAL,
  370.                    &timer, NULL);
  371.       }
  372.       else 
  373.       {
  374.     if (selectNote == 0) 
  375.     {
  376.       XDrawRectangle(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gcLine, (tlFrame->startX  / tlFrame->zoomLevel) - tlFrame->canvasStart, 
  377.              tlFrame->startY, (tlFrame->endX - tlFrame->startX) / tlFrame->zoomLevel,
  378.              tlFrame->endY - tlFrame->startY);
  379.       tlFrame->endX = xPos;
  380.       if (yPos < tlFrame->startY)                    /* If the new Y position is smaller (higher) than the start position, */
  381.         tlFrame->endY = yPos;
  382.       else 
  383.         tlFrame->endY = yPos + (IconHeight + IconGap);
  384.       if (tlFrame->endY > 
  385.           (tlFrame->numberOfApps * (IconHeight + IconGap)))        /* Set the maximum y position to be the bottom line of the last instrument track */
  386.         tlFrame->endY = tlFrame->numberOfApps * (IconHeight + IconGap);
  387.       else if (tlFrame->endY < 0)                    /* Check that the Y position does not go off the canvas */
  388.         tlFrame->endY = 0;
  389.       XDrawRectangle(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gcLine, (tlFrame->startX / tlFrame->zoomLevel) - tlFrame->canvasStart, 
  390.              tlFrame->startY, (tlFrame->endX - tlFrame->startX) / tlFrame->zoomLevel,
  391.              tlFrame->endY - tlFrame->startY);
  392.     }
  393.     else 
  394.     {
  395.       if (wasLeftDown > 1) 
  396.         XDrawRectangle(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gcLine, 
  397.                (tlFrame->startX  / tlFrame->zoomLevel) - tlFrame->canvasStart - noteOffset, 
  398.                currentInst->cableStart - NoteHeight/2, 
  399.                (currentInst->infoNote->ms->duration * PixelsPerSecond) / (2 * tlFrame->zoomLevel),
  400.                NoteHeight);
  401.       tlFrame->startX = xPos;
  402.       if (tlFrame->startX / tlFrame->zoomLevel - noteOffset < 0) 
  403.         tlFrame->startX = noteOffset * tlFrame->zoomLevel;
  404.       XDrawRectangle(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gcLine, 
  405.              (tlFrame->startX  / tlFrame->zoomLevel) - tlFrame->canvasStart - noteOffset, 
  406.              currentInst->cableStart - NoteHeight/2, 
  407.              (currentInst->infoNote->ms->duration * PixelsPerSecond) / (2 * tlFrame->zoomLevel),
  408.              NoteHeight);
  409.     }
  410.       }
  411.       wasLeftDown ++;
  412.     }
  413.   }
  414.   return notify_next_event_func(win, (Notify_event) event, arg, type);
  415. }
  416.  
  417. /*
  418.  * Repaint callback function for `DrawCanvas'.
  419.  * The function clear the whole Draw canvas and redraw all the instrument cables and notes.
  420.  * Since the playback head has been cleared, it will be redrawn at its last position after the new notes are drawn.
  421.  */
  422. void DrawCanvasRepaintHandler(canvas, paint_window, display, xid, rects)
  423.      Canvas          canvas;
  424.      Xv_window       paint_window;
  425.      Display         *display;
  426.      Window          xid;
  427.      Xv_xrectlist    *rects;
  428. {
  429.   Instrument     *instrument;
  430.   int         templastX;
  431.   TimeLineFramePtr tlFrame;
  432.   TimeLine_window_objects    *ip = (TimeLine_window_objects *) xv_get(canvas, XV_KEY_DATA, INSTANCE);
  433.  
  434.   
  435.   tlFrame = TimeLineWindow[xv_get(ip->controls, PANEL_CLIENT_DATA)];
  436.   XClearWindow(tlFrame->dpyDraw, tlFrame->xidDraw);            /* Clear the whole display */
  437.   DrawGrid (tlFrame);
  438.   for (instrument = tlFrame->instHead; instrument != NULL; instrument = instrument->next) 
  439.     InstrumentDraw(instrument, tlFrame);
  440.   templastX = tlFrame->lastX;                        /* Store the last playback head position */
  441.   tlFrame->lastX = -1;
  442.   DrawPlaybackHead (templastX, tlFrame);
  443.   if (xv_get(tlFrame->TimeLine_window->controls, PANEL_CLIENT_DATA) == 0) /* Draw the ending line for the clipboard frame */
  444.   {
  445.     XDrawLine(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gc, clipDuration, 0,
  446.           clipDuration, tlFrame->numberOfApps * 72 + FirstCableYPosition);
  447.     XDrawLine(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gc, clipDuration + 2, 0,
  448.           clipDuration + 2, tlFrame->numberOfApps * 72 + FirstCableYPosition);
  449.   }
  450.   if (tlFrame->areaSelected && (tlFrame->startX / tlFrame->zoomLevel >= tlFrame->canvasStart)
  451.       && (tlFrame->startX / tlFrame->zoomLevel <= tlFrame->canvasStart + tlFrame->TimeLineLength))
  452.     XFillRectangle(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gcLine, (tlFrame->startX / tlFrame->zoomLevel) - tlFrame->canvasStart, 
  453.            tlFrame->startY, (tlFrame->endX  - tlFrame->startX) / tlFrame->zoomLevel, 
  454.            tlFrame->endY - tlFrame->startY);
  455. }
  456.  
  457. /*
  458.  * Function to draw the new position of the playback head.  First, the old line is erased (drawing over it in XOR mode will restore the original canvas
  459.  * condition), the new line is then drawn in the new position, and the playback head position variable (lastX) is set to the new position.
  460.  * The parameter newPosition is where the new position is to be.
  461.  * Called by DrawCanvasEventHandler (canvas.c), DeleteNote (note.c), TimerNotify (play.c)
  462.  */
  463. void DrawPlaybackHead(newPosition, tlFrame)
  464.      int newPosition;
  465.      TimeLineFramePtr tlFrame;
  466. {
  467.   int playbackHeadPosition;
  468.   int oldPosition;
  469.   int min, sec;
  470.   char buf[4];
  471.  
  472.   playbackHeadPosition = (newPosition / tlFrame->zoomLevel) - tlFrame->canvasStart;
  473.   oldPosition = (tlFrame->lastX / tlFrame->zoomLevel) - tlFrame->canvasStart;
  474.   if (oldPosition >= 0 && oldPosition <= tlFrame->TimeLineLength)   /* Clear the old playback head if necessary */
  475.     XDrawLine(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gcLine, oldPosition, 0,
  476.           oldPosition, tlFrame->numberOfApps * 72 + FirstCableYPosition);
  477.   if (playbackHeadPosition >= 0 && playbackHeadPosition <= tlFrame->TimeLineLength) 
  478.     XDrawLine(tlFrame->dpyDraw, tlFrame->xidDraw, tlFrame->gcLine, playbackHeadPosition, 0,
  479.           playbackHeadPosition, tlFrame->numberOfApps * 72 + FirstCableYPosition);
  480.   tlFrame->lastX = newPosition;
  481.   min = 0;                                /* Calculate the time to display in the pause marker info window */
  482.   sec = newPosition / PixelsPerSecond;
  483.   if (sec < 0) 
  484.     sec = 0;
  485.   while (sec >= 60) 
  486.   {
  487.     min ++;
  488.     sec -= 60;
  489.   }
  490.   sprintf (buf, "%02d", sec);
  491.   xv_set(tlFrame->PausePopup->IPSecText, PANEL_VALUE, buf, NULL);
  492.   sprintf (buf, "%4d", min);
  493.   xv_set(tlFrame->PausePopup->IPMinText, PANEL_VALUE, buf, NULL);
  494. }
  495.